#include "stdafx.h"
#include "TabHeaderEx.h"


TabHeaderEx::TabHeaderEx(void)
{
	mnFirstShowIndex = 0;
	mdwItemFixedWidth = 80;
	mdwItemWidth = mdwItemFixedWidth;
	mnSelectedIndex = 0;
	mhWnd = NULL;
	mnPrevState = mnNextState = TabWidgetState::NORMAL;
	mnPrevNextWidth = 20;
	mnIconWidth = 20;
	mnCloseWidth = 20;
	mclrBkgnd = TAB_COLOR_DEFAULT;
	mnNotifyMessage = WM_NULL;
}

TabHeaderEx::~TabHeaderEx(void)
{
}

void TabHeaderEx::SetRect(CRect rect)
{
	mRect = rect;
	ResizeAllItems();
}

void TabHeaderEx::SetFixedItemWidth(DWORD dwWidth)
{
	mdwItemFixedWidth = dwWidth;
	ResizeAllItems();
}

void TabHeaderEx::SetParentHwnd(HWND hWnd)
{
	mhWnd = hWnd;
}

void TabHeaderEx::SetBkgndColor(COLORREF clrBkgnd)
{
	mclrBkgnd = clrBkgnd;
}

void TabHeaderEx::SetNotifyMessage(UINT nMsg)
{
	mnNotifyMessage = nMsg;
}

BOOL TabHeaderEx::InsertItem(UINT nID, LPCTSTR lpszTitle, LPCTSTR lpszIcon, UINT nIDInsertAfter/* = -1*/, BOOL bClosable/* = FALSE*/, COLORREF clrBkgnd/* = ITEM_COLOR_DEFAULT*/)
{
	
	TabItem* pItem = NewItem(nID, lpszTitle, lpszIcon, bClosable);
	pItem->clrBkgnd = clrBkgnd;
	if ( nIDInsertAfter == -1 )
	{
		mItems.Add(pItem);
	}
	else
	{
		INT_PTR nIndex = GetItemIndexByID(nIDInsertAfter);
		mItems.InsertAt(nIndex+1, pItem);
	}

	ResizeAllItems();

	NotifyItemChanged(ItemChangedType::INSERT, nID);

	SelectItem(nID);

	return TRUE;
}

void TabHeaderEx::RemoveItem(UINT nID)
{
	TabItem* pItem = GetItemByID(nID);
	if ( pItem == NULL )
		return;

	INT_PTR count = mItems.GetUpperBound();
	if ( (pItem->nIndex+mnMaxShowItems) > count )
	{
		mnFirstShowIndex--;
		if ( mnFirstShowIndex < 0 )
		{
			mnFirstShowIndex = 0;
		}
	}

	mItems.RemoveAt(pItem->nIndex);
	delete pItem;

	ResizeAllItems();

	SelectItemByIndex(mnSelectedIndex);
}

BOOL TabHeaderEx::IsItemEmpty(UINT nID)
{
	TabItem* pItem = GetItemByID(nID);
	return ( pItem == NULL );
}

void TabHeaderEx::SelectItem(UINT nID)
{
	TabItem* pItem = GetItemByID(nID);
	if ( pItem == NULL )
		return;

	SelectItemByIndex( pItem->nIndex );	
}

void TabHeaderEx::SelectItemByIndex(INT_PTR nIndex)
{
	if ( mItems.IsEmpty() )
		return;

	if ( nIndex < 0 )
		nIndex = mnFirstShowIndex;
	else if ( nIndex > mItems.GetUpperBound() )
		nIndex = mItems.GetUpperBound();
	else
	{
		if ( nIndex < mnFirstShowIndex )
			mnFirstShowIndex = nIndex;
		else if ( nIndex >= (mnFirstShowIndex + mnMaxShowItems) )
		{
			mnFirstShowIndex = nIndex - mnMaxShowItems + 1;
		}
	}

	if ( mnSelectedIndex != nIndex)
	{
		TabItem* pSelectedItem = GetItemByIndex( mnSelectedIndex );
		if ( pSelectedItem != NULL )
			pSelectedItem->nState = TabWidgetState::NORMAL;
	}

	TabItem* pItem = GetItemByIndex(nIndex);
	pItem->nState = TabWidgetState::SELECTED;
	mnSelectedIndex = nIndex;
	NotifyItemChanged(ItemChangedType::SELCHANGED, pItem->nSelfID);
}

BOOL TabHeaderEx::NotifyItemChanged(UINT nType, UINT nID)
{
	if ( mnNotifyMessage == WM_NULL )
		return FALSE;

	if ( !IsWindow(mhWnd) )
		return FALSE;

	UINT nNotifyID = MAKEWPARAM(nID, nType);
	return PostMessage(mhWnd, mnNotifyMessage, nNotifyID, mnSelfID);
}

INT_PTR TabHeaderEx::GetItemCount()
{
	return mItems.GetSize();
}

INT_PTR TabHeaderEx::GetItemIndexByID(UINT nID)
{
	TabItem* pItem = GetItemByID(nID);
	if ( pItem == NULL )
		return -1;

	return pItem->nIndex;
}

UINT TabHeaderEx::GetItemIDByIndex(INT_PTR nIndex)
{
	if ( nIndex > mItems.GetUpperBound() )
		return -1;

	TabItem* pItem = mItems.GetAt(nIndex);
	return pItem->nSelfID;
}

void TabHeaderEx::ResizeAllItems()
{
	INT_PTR nCount = mItems.GetSize();

	mnMaxShowItems = mRect.Width()/mdwItemFixedWidth;

	if ( nCount > mnMaxShowItems )
	{
		mdwItemWidth = ( mRect.Width() - 2*mnPrevNextWidth )/mnMaxShowItems;

		if ( (mnFirstShowIndex + mnMaxShowItems) >= nCount )
		{
			mnFirstShowIndex = nCount - mnMaxShowItems;
		}
	}
	else
	{
		mdwItemWidth = mdwItemFixedWidth;
		mnFirstShowIndex = 0;
	}

	SelectItemByIndex(mnSelectedIndex);
}

void TabHeaderEx::Render(HDC hDC)
{
	DrawBkgnd(hDC, mRect);
	
	TabItem* pItem = NULL;
	INT_PTR count = mItems.GetCount();
	INT_PTR nMaxShowIndex = count;

	int nAdjustWidth = 0;

	CRect rcItem = mRect;
	if ( count > mnMaxShowItems )
	{
		CRect rcMove;
		rcMove = mRect;
		rcMove.right = rcMove.left + mnPrevNextWidth;
		DrawPrev(hDC, rcMove);
		rcMove = mRect;
		rcMove.left = rcMove.right - mnPrevNextWidth;
		DrawNext(hDC, rcMove);
		rcItem.DeflateRect(mnPrevNextWidth, 0, mnPrevNextWidth, 0);
		nMaxShowIndex = mnMaxShowItems + mnFirstShowIndex;
		nAdjustWidth = mRect.Width() - 2*mnPrevNextWidth - mdwItemWidth*mnMaxShowItems;
	}

	rcItem.right = rcItem.left + mdwItemWidth;
	for ( INT_PTR i = mnFirstShowIndex; i < nMaxShowIndex; i++ )
	{
		pItem = mItems.GetAt(i);
		if ( pItem->nState == TabWidgetState::SELECTED )
		{
			rcItem.InflateRect(0, 0, nAdjustWidth, 0);
		}

		DrawItem(hDC, pItem, rcItem);
		rcItem.OffsetRect( rcItem.Width(), 0 );

		if ( pItem->nState == TabWidgetState::SELECTED )
		{
			rcItem.DeflateRect(0, 0, nAdjustWidth, 0);
		}
	}
}

void TabHeaderEx::DrawBkgnd(HDC hDC, CRect rect)
{
	HBRUSH hBrush = CreateSolidBrush(mclrBkgnd);
	::FillRect(hDC, rect, hBrush);
	DeleteObject(hBrush);
	if ( !mimgBkgnd.IsNull() )
		mimgBkgnd.StretchBlt(hDC, rect);
}

void TabHeaderEx::DrawItem(HDC hDC, TabItem* pItem, CRect rect)
{
	CRect rcImg(0, 0, 0, 0);

	HBRUSH hBrush = CreateSolidBrush(pItem->clrBkgnd);
	::FillRect(hDC, rect, hBrush);
	DeleteObject(hBrush);

	if ( pItem->nState == TabWidgetState::HOVER )
	{
		rcImg.right = mimgItemHover.GetWidth();
		rcImg.bottom = mimgItemHover.GetHeight();
		mimgItemHover.AlphaBlend(hDC, rect, rcImg);
	}
	else if ( pItem->nState == TabWidgetState::SELECTED )
	{
		rcImg.right = mimgItemSelected.GetWidth();
		rcImg.bottom = mimgItemSelected.GetHeight();
		mimgItemSelected.AlphaBlend(hDC, rect, rcImg);
		hBrush = CreateSolidBrush(RGB(255,255,255));
		::FillRect(hDC, rect, hBrush);
		DeleteObject(hBrush);
	}
	else
	{
		rcImg.right = mimgItemNormal.GetWidth();
		rcImg.bottom = mimgItemNormal.GetHeight();
		mimgItemNormal.AlphaBlend(hDC, rect, rcImg);
	}

	CRect rcWidget = rect;
	if ( !pItem->imgIcon.IsNull() )
	{
		rcImg.right = pItem->imgIcon.GetWidth();
		rcImg.bottom = pItem->imgIcon.GetHeight();

		rcWidget.right = rcWidget.left + mnIconWidth;
		
		pItem->imgIcon.AlphaBlend(hDC, rcWidget, rcImg);

		rect.DeflateRect(mnIconWidth, 0, 0, 0);
	}
	
	if ( pItem->bClosable )
	{
		rcWidget = rect;
		rcWidget.left = rcWidget.right - mnCloseWidth;

		DrawClose(hDC, rcWidget, pItem->nCloseState);

		rect.DeflateRect(0, 0, mnCloseWidth, 0);
	}

	if ( !pItem->txtTitle.IsEmpty() )
	{
		DrawText(hDC, pItem->txtTitle, pItem->txtTitle.GetLength(), rect, DT_CENTER|DT_VCENTER|DT_END_ELLIPSIS);
	}
}

void TabHeaderEx::DrawPrev(HDC hDC, CRect rect)
{
	CRect rcImg(0, 0, 0, 0);
	switch( mnPrevState )
	{
	case TabWidgetState::HOVER:
		{
			rcImg.right = mimgPrevHover.GetWidth();
			rcImg.bottom = mimgPrevHover.GetHeight();
			mimgPrevHover.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	case TabWidgetState::DOWN:
		{
			rcImg.right = mimgPrevDown.GetWidth();
			rcImg.bottom = mimgPrevDown.GetHeight();
			mimgPrevDown.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	default:
		{
			rcImg.right = mimgPrevNormal.GetWidth();
			rcImg.bottom = mimgPrevNormal.GetHeight();
			mimgPrevNormal.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	}
}

void TabHeaderEx::DrawNext(HDC hDC, CRect rect)
{
	CRect rcImg(0, 0, 0, 0);
	switch( mnNextState )
	{
	case TabWidgetState::HOVER:
		{
			rcImg.right = mimgNextHover.GetWidth();
			rcImg.bottom = mimgNextHover.GetHeight();
			mimgNextHover.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	case TabWidgetState::DOWN:
		{
			rcImg.right = mimgNextDown.GetWidth();
			rcImg.bottom = mimgNextDown.GetHeight();
			mimgNextDown.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	default:
		{
			rcImg.right = mimgNextNormal.GetWidth();
			rcImg.bottom = mimgNextNormal.GetHeight();
			mimgNextNormal.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	}
}

void TabHeaderEx::DrawClose(HDC hDC, CRect rect, UINT nState)
{
	CRect rcImg(0, 0, 0, 0);
	switch( nState )
	{
	case TabWidgetState::HOVER:
		{
			rcImg.right = mimgCloseHover.GetWidth();
			rcImg.bottom = mimgCloseHover.GetHeight();
			mimgCloseHover.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	case TabWidgetState::DOWN:
		{
			rcImg.right = mimgCloseDown.GetWidth();
			rcImg.bottom = mimgCloseDown.GetHeight();
			mimgCloseDown.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	default:
		{
			rcImg.right = mimgCloseNormal.GetWidth();
			rcImg.bottom = mimgCloseNormal.GetHeight();
			mimgCloseNormal.AlphaBlend(hDC, rect, rcImg);
		}
		break;
	}
}

void TabHeaderEx::UpdateState(CPoint point, int mkf)
{
	TabItem* pItem = NULL;
	INT_PTR count = mItems.GetCount();
	BOOL bChangeNormal = TRUE;
	BOOL bSelChanged = FALSE;
	INT_PTR nSelIndex = -1;

	CRect rcItem = mRect;
	INT_PTR nMaxShowIndex = count;

	mnPrevState = TabWidgetState::NORMAL;
	mnNextState = TabWidgetState::NORMAL;

	int nAdjustWidth = 0;
	if ( count > mnMaxShowItems )
	{
		CRect rcPrev, rcNext;
		rcPrev = mRect;
		rcPrev.right = rcPrev.left + mnPrevNextWidth;
		rcNext = mRect;
		rcNext.left = rcNext.right - mnPrevNextWidth;
		if ( rcPrev.PtInRect( point ) )
		{
			if ( mnFirstShowIndex > 0 )
			{
				switch( mkf )
				{
				case MouseKeyFlag::MOVE:
				case MouseKeyFlag::LBUTTONUP:
					{
						mnPrevState = TabWidgetState::HOVER;
					}
					break;
				case MouseKeyFlag::LBUTTONDOWN:
					{
						mnPrevState = TabWidgetState::DOWN;
						mnFirstShowIndex--;
						if ( mnSelectedIndex >= (mnFirstShowIndex+mnMaxShowItems) )
						{
							bSelChanged = TRUE;
							nSelIndex = mnFirstShowIndex;
						}
					}
					break;
				default:
					break;
				}
			}

			bChangeNormal = FALSE;
		}
		else if ( rcNext.PtInRect( point ) )
		{
			if ( (mnFirstShowIndex + mnMaxShowItems) < count )
			{
				switch( mkf )
				{
				case MouseKeyFlag::MOVE:
				case MouseKeyFlag::LBUTTONUP:
					{
						mnNextState = TabWidgetState::HOVER;
					}
					break;
				case MouseKeyFlag::LBUTTONDOWN:
					{
						mnNextState = TabWidgetState::DOWN;
						mnFirstShowIndex++;
						if ( mnSelectedIndex < mnFirstShowIndex )
						{
							bSelChanged = TRUE;
							nSelIndex = mnFirstShowIndex;
						}
					}
					break;
				default:
					break;
				}
			}
			bChangeNormal = FALSE;
		}
		rcItem.DeflateRect(mnPrevNextWidth, 0, mnPrevNextWidth, 0);
		nMaxShowIndex = mnMaxShowItems + mnFirstShowIndex;
		nAdjustWidth = mRect.Width() - 2*mnPrevNextWidth - mdwItemWidth*mnMaxShowItems;
	}

	if ( nSelIndex == -1 )
		nSelIndex = mnSelectedIndex;

	BOOL bAdjust = FALSE;
	rcItem.right = rcItem.left + mdwItemWidth;
	for ( INT_PTR i = mnFirstShowIndex; i < nMaxShowIndex; i++ )
	{
		pItem = mItems.GetAt(i);

		if ( pItem->nState == TabWidgetState::SELECTED )
		{
			rcItem.InflateRect(0, 0, nAdjustWidth, 0);
			bAdjust = TRUE;
		}

		pItem->nCloseState = TabWidgetState::NORMAL;
		if ( mkf == MouseKeyFlag::LBUTTONDOWN && bChangeNormal)
		{
			pItem->nState = TabWidgetState::NORMAL;
		}
		if ( rcItem.PtInRect(point) )
		{
			BOOL bInClose = FALSE;
			if ( pItem->bClosable )
			{
				CRect rcClose;
				rcClose = rcItem;
				rcClose.left = rcItem.right - mnCloseWidth;
				if ( rcClose.PtInRect(point) )
				{
					switch( mkf )
					{
					case MouseKeyFlag::MOVE:
						pItem->nCloseState = TabWidgetState::HOVER;
						break;
					case MouseKeyFlag::LBUTTONDOWN:
						{
							pItem->nCloseState = TabWidgetState::DOWN;
							NotifyItemChanged(ItemChangedType::REMOVE, pItem->nSelfID);
							bSelChanged = TRUE;
							bInClose = TRUE;
						}
						break;
					default:
						break;
					}
				}
				else if ( mkf == MouseKeyFlag::LBUTTONDBCLK )
				{
					NotifyItemChanged(ItemChangedType::REMOVE, pItem->nSelfID);
					bSelChanged = TRUE;
				}
			}

			if ( !bInClose )
			{
				if ( pItem->nState != TabWidgetState::SELECTED )
				{
					if ( mkf == MouseKeyFlag::MOVE )
						pItem->nState = TabWidgetState::HOVER;
					else if ( mkf == MouseKeyFlag::LBUTTONDOWN )
					{
						bSelChanged = TRUE;
						nSelIndex = i;
					}
				}
			}
		}
		else
		{
			if ( pItem->nState != TabWidgetState::SELECTED )
				pItem->nState = TabWidgetState::NORMAL;
		}

		rcItem.OffsetRect(rcItem.Width(), 0);

		if ( bAdjust )
		{
			rcItem.DeflateRect(0, 0, nAdjustWidth, 0);
		}
	}

	if ( bSelChanged )
	{
		SelectItemByIndex(nSelIndex);
	}
}

TabItem* TabHeaderEx::NewItem(UINT nID, LPCTSTR lpszTitle, LPCTSTR lpszIcon, BOOL bClosable/* = FALSE*/)
{
	TabItem* pItem = new TabItem;
	pItem->nSelfID = nID;
	if ( lpszTitle )
		pItem->txtTitle = lpszTitle;
	if ( lpszIcon )
		pItem->imgIcon.Load(lpszIcon);
	pItem->nState = TabWidgetState::NORMAL;
	pItem->nIndex = 0;
	pItem->bClosable = bClosable;
	pItem->nCloseState = TabWidgetState::NORMAL;

	return pItem;
}

TabItem* TabHeaderEx::GetItemByID(UINT nID)
{
	TabItem* pItem = NULL;
	INT_PTR count = mItems.GetCount();
	for ( INT_PTR i = 0; i < count; i++ )
	{
		pItem = mItems.GetAt(i);
		if ( pItem->nSelfID == nID )
		{
			pItem->nIndex = i;
			break;
		}
		pItem = NULL;
	}
	return pItem;
}

TabItem* TabHeaderEx::GetItemByIndex(INT_PTR nIndex)
{
	TabItem* pItem = NULL;

	if ( nIndex < 0 || nIndex > mItems.GetUpperBound() )
		return pItem;

	pItem = mItems.GetAt( nIndex );

	return pItem;
}

